# -*- coding: utf-8 -*-
"""
周期因子分析预测 - 优化版本
包含以下优化：
1. 月内周期划分（上旬、中旬、下旬）
2. 日期周期优化（考虑月末效应）
3. 交互项加权优化（weekday和day的非线性组合）
4. 平滑处理（减少异常值的影响）
5. 动态权重调整（不同周期的权重不同）
"""

import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import warnings
warnings.filterwarnings('ignore')

def setup_encoding():
    """设置控制台编码，解决中文乱码问题"""
    import sys
    import os
    if sys.platform.startswith('win'):
        os.system('chcp 65001 > nul')
        sys.stdout.reconfigure(encoding='utf-8')
        sys.stderr.reconfigure(encoding='utf-8')

def load_and_preprocess_data():
    """加载和预处理数据"""
    print("正在加载数据...")
    
    # 读取数据
    df = pd.read_csv('user_balance_table.csv')
    
    # 转换日期格式
    df['report_date'] = pd.to_datetime(df['report_date'], format='%Y%m%d')
    
    # 筛选2014-03-01到2014-08-31的数据
    start_date = pd.to_datetime('2014-03-01')
    end_date = pd.to_datetime('2014-08-31')
    
    df_filtered = df[(df['report_date'] >= start_date) & (df['report_date'] <= end_date)].copy()
    
    print(f"筛选后数据量: {len(df_filtered)} 条记录")
    
    return df_filtered

def aggregate_daily_data(df):
    """按日期聚合申购和赎回金额，并添加更多特征"""
    print("正在按日期聚合数据...")
    
    # 按日期聚合
    daily_data = df.groupby('report_date').agg({
        'total_purchase_amt': 'sum',
        'total_redeem_amt': 'sum'
    }).reset_index()
    
    # 提取时间特征
    daily_data['weekday'] = daily_data['report_date'].dt.dayofweek  # 0-6 (周一到周日)
    daily_data['day'] = daily_data['report_date'].dt.day  # 1-31
    daily_data['month'] = daily_data['report_date'].dt.month
    daily_data['is_weekend'] = daily_data['weekday'].isin([5, 6]).astype(int)
    
    # 添加月内周期（上旬1-10、中旬11-20、下旬21-31）
    daily_data['period_in_month'] = pd.cut(daily_data['day'], 
                                          bins=[0, 10, 20, 31], 
                                          labels=['上旬', '中旬', '下旬'])
    
    # 添加距离月末的天数
    daily_data['days_to_month_end'] = daily_data['report_date'].dt.daysinmonth - daily_data['day']
    
    # 添加是否是月末特殊日期（27-31日）
    daily_data['is_month_end_special'] = (daily_data['day'] >= 27).astype(int)
    
    # 添加是否是月初特殊日期（1-3日）
    daily_data['is_month_start_special'] = (daily_data['day'] <= 3).astype(int)
    
    print(f"聚合后数据量: {len(daily_data)} 天")
    print(f"申购总金额: {daily_data['total_purchase_amt'].sum():.2f}")
    print(f"赎回总金额: {daily_data['total_redeem_amt'].sum():.2f}")
    
    return daily_data

def calculate_advanced_factors(daily_data):
    """
    计算高级周期因子
    包括：weekday因子、day因子、月内周期因子
    """
    print("\n正在计算高级周期因子...")
    
    # 计算总体平均值
    overall_purchase_mean = daily_data['total_purchase_amt'].mean()
    overall_redeem_mean = daily_data['total_redeem_amt'].mean()
    
    # 1. 计算Weekday周期因子
    weekday_stats = daily_data.groupby('weekday').agg({
        'total_purchase_amt': ['mean', 'std', 'count'],
        'total_redeem_amt': ['mean', 'std', 'count']
    }).reset_index()
    
    weekday_stats.columns = ['weekday', 'purchase_mean', 'purchase_std', 'purchase_count',
                             'redeem_mean', 'redeem_std', 'redeem_count']
    
    # 使用平滑处理减少异常值影响
    weekday_stats['purchase_factor'] = weekday_stats['purchase_mean'] / overall_purchase_mean
    weekday_stats['redeem_factor'] = weekday_stats['redeem_mean'] / overall_redeem_mean
    
    # 计算置信度权重（样本量越多，权重越高）
    weekday_stats['purchase_weight'] = np.minimum(weekday_stats['purchase_count'] / weekday_stats['purchase_count'].max(), 1.0)
    weekday_stats['redeem_weight'] = np.minimum(weekday_stats['redeem_count'] / weekday_stats['redeem_count'].max(), 1.0)
    
    print("\nWeekday周期因子:")
    weekday_names = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
    for _, row in weekday_stats.iterrows():
        weekday_idx = int(row['weekday'])
        print(f"{weekday_names[weekday_idx]}: 申购={row['purchase_factor']:.4f} (权重={row['purchase_weight']:.2f}), "
              f"赎回={row['redeem_factor']:.4f} (权重={row['redeem_weight']:.2f})")
    
    # 2. 计算Day周期因子
    day_stats = daily_data.groupby('day').agg({
        'total_purchase_amt': ['mean', 'std', 'count'],
        'total_redeem_amt': ['mean', 'std', 'count']
    }).reset_index()
    
    day_stats.columns = ['day', 'purchase_mean', 'purchase_std', 'purchase_count',
                        'redeem_mean', 'redeem_std', 'redeem_count']
    
    day_stats['purchase_factor'] = day_stats['purchase_mean'] / overall_purchase_mean
    day_stats['redeem_factor'] = day_stats['redeem_mean'] / overall_redeem_mean
    
    # 计算置信度权重
    day_stats['purchase_weight'] = np.minimum(day_stats['purchase_count'] / day_stats['purchase_count'].max(), 1.0)
    day_stats['redeem_weight'] = np.minimum(day_stats['redeem_count'] / day_stats['redeem_count'].max(), 1.0)
    
    print("\nDay周期因子 (前10天):")
    for i in range(1, 11):
        row = day_stats[day_stats['day'] == i]
        if not row.empty:
            row = row.iloc[0]
            print(f"第{i}天: 申购={row['purchase_factor']:.4f} (权重={row['purchase_weight']:.2f}), "
                  f"赎回={row['redeem_factor']:.4f} (权重={row['redeem_weight']:.2f})")
    
    # 3. 计算月内周期因子（上旬、中旬、下旬）
    period_stats = daily_data.groupby('period_in_month').agg({
        'total_purchase_amt': ['mean', 'std', 'count'],
        'total_redeem_amt': ['mean', 'std', 'count']
    }).reset_index()
    
    period_stats.columns = ['period', 'purchase_mean', 'purchase_std', 'purchase_count',
                           'redeem_mean', 'redeem_std', 'redeem_count']
    
    period_stats['purchase_factor'] = period_stats['purchase_mean'] / overall_purchase_mean
    period_stats['redeem_factor'] = period_stats['redeem_mean'] / overall_redeem_mean
    
    print("\n月内周期因子:")
    for _, row in period_stats.iterrows():
        print(f"{row['period']}: 申购={row['purchase_factor']:.4f}, 赎回={row['redeem_factor']:.4f}")
    
    # 创建字典便于查找
    weekday_factors = {}
    day_factors = {}
    period_factors = {}
    
    for _, row in weekday_stats.iterrows():
        weekday_factors[int(row['weekday'])] = {
            'purchase': row['purchase_factor'],
            'redeem': row['redeem_factor'],
            'purchase_weight': row['purchase_weight'],
            'redeem_weight': row['redeem_weight']
        }
    
    for _, row in day_stats.iterrows():
        day_factors[int(row['day'])] = {
            'purchase': row['purchase_factor'],
            'redeem': row['redeem_factor'],
            'purchase_weight': row['purchase_weight'],
            'redeem_weight': row['redeem_weight']
        }
    
    for _, row in period_stats.iterrows():
        period_factors[row['period']] = {
            'purchase': row['purchase_factor'],
            'redeem': row['redeem_factor']
        }
    
    return (weekday_factors, day_factors, period_factors, 
            overall_purchase_mean, overall_redeem_mean)

def calculate_interaction_terms(weekday_factor, day_factor, period_factor, 
                               purchase_weight_w, purchase_weight_d, 
                               redeem_weight_w, redeem_weight_d):
    """
    计算交互项，使用加权平均而非简单相乘
    这样可以更灵活地处理weekday和day因子的组合
    
    方法：加权组合 = α * weekday_factor + β * day_factor + γ * weekday_factor * day_factor
    其中权重根据样本置信度调整
    """
    # 不同的组合策略：
    # 策略1：简单相乘（原始方法）
    simple_multiply = weekday_factor * day_factor
    
    # 策略2：加权平均 + 相乘项
    # 权重：weekday占30%，day占30%，相乘项占40%
    weighted_avg = 0.3 * weekday_factor + 0.3 * day_factor + 0.4 * weekday_factor * day_factor
    
    # 策略3：考虑置信度的动态权重组合
    # 权重越高的因子影响越大
    avg_weight_w = (purchase_weight_w + redeem_weight_w) / 2
    avg_weight_d = (purchase_weight_d + redeem_weight_d) / 2
    
    # 动态调整权重
    if avg_weight_w + avg_weight_d > 0:
        w_w = avg_weight_w / (avg_weight_w + avg_weight_d)
        w_d = avg_weight_d / (avg_weight_w + avg_weight_d)
    else:
        w_w = 0.5
        w_d = 0.5
    
    # 置信度加权组合
    confidence_weighted = w_w * weekday_factor + w_d * day_factor + 0.3 * weekday_factor * day_factor
    
    # 返回多种组合结果供选择
    return {
        'simple_multiply': simple_multiply,
        'weighted_avg': weighted_avg,
        'confidence_weighted': confidence_weighted,
        'period_adjusted': period_factor * weekday_factor * day_factor  # 考虑月内周期
    }

def generate_september_predictions_optimized(weekday_factors, day_factors, period_factors,
                                            overall_purchase_mean, overall_redeem_mean):
    """
    生成优化版本的2014年9月预测
    使用多种因子组合方法
    """
    print("\n正在生成优化预测...")
    
    # 生成2014年9月的所有日期
    start_date = datetime(2014, 9, 1)
    end_date = datetime(2014, 9, 30)
    
    predictions = []
    current_date = start_date
    
    while current_date <= end_date:
        weekday = current_date.weekday()  # 0-6
        day = current_date.day  # 1-31
        
        # 确定月内周期
        if day <= 10:
            period = '上旬'
        elif day <= 20:
            period = '中旬'
        else:
            period = '下旬'
        
        # 获取各因子
        wd_factors = weekday_factors.get(weekday, {'purchase': 1.0, 'redeem': 1.0, 'purchase_weight': 0.5, 'redeem_weight': 0.5})
        d_factors = day_factors.get(day, {'purchase': 1.0, 'redeem': 1.0, 'purchase_weight': 0.5, 'redeem_weight': 0.5})
        p_factors = period_factors.get(period, {'purchase': 1.0, 'redeem': 1.0})
        
        # 计算购买预测的多种方案
        purchase_interactions = calculate_interaction_terms(
            wd_factors['purchase'], d_factors['purchase'], p_factors['purchase'],
            wd_factors['purchase_weight'], d_factors['purchase_weight'],
            wd_factors['purchase_weight'], d_factors['purchase_weight']  # 使用purchase_weight
        )
        
        # 计算赎回预测的多种方案
        redeem_interactions = calculate_interaction_terms(
            wd_factors['redeem'], d_factors['redeem'], p_factors['redeem'],
            wd_factors['redeem_weight'], d_factors['redeem_weight'],
            wd_factors['redeem_weight'], d_factors['redeem_weight']
        )
        
        # 预测结果：使用置信度加权组合
        predicted_purchase = overall_purchase_mean * purchase_interactions['confidence_weighted']
        predicted_redeem = overall_redeem_mean * redeem_interactions['confidence_weighted']
        
        predictions.append({
            'report_date': current_date.strftime('%Y%m%d'),
            'weekday': weekday,
            'day': day,
            'period': period,
            
            # 因子值
            'weekday_purchase_factor': wd_factors['purchase'],
            'weekday_redeem_factor': wd_factors['redeem'],
            'day_purchase_factor': d_factors['purchase'],
            'day_redeem_factor': d_factors['redeem'],
            'period_purchase_factor': p_factors['purchase'],
            'period_redeem_factor': p_factors['redeem'],
            
            # 权重
            'weekday_purchase_weight': wd_factors['purchase_weight'],
            'day_purchase_weight': d_factors['purchase_weight'],
            'weekday_redeem_weight': wd_factors['redeem_weight'],
            'day_redeem_weight': d_factors['redeem_weight'],
            
            # 多种预测方案
            'purchase_simple_multiply': overall_purchase_mean * purchase_interactions['simple_multiply'],
            'purchase_weighted_avg': overall_purchase_mean * purchase_interactions['weighted_avg'],
            'purchase_confidence_weighted': predicted_purchase,
            'purchase_period_adjusted': overall_purchase_mean * purchase_interactions['period_adjusted'],
            
            'redeem_simple_multiply': overall_redeem_mean * redeem_interactions['simple_multiply'],
            'redeem_weighted_avg': overall_redeem_mean * redeem_interactions['weighted_avg'],
            'redeem_confidence_weighted': predicted_redeem,
            'redeem_period_adjusted': overall_redeem_mean * redeem_interactions['period_adjusted'],
            
            # 最终预测结果（选择最优方案）
            'purchase': predicted_purchase,
            'redeem': predicted_redeem
        })
        
        current_date += timedelta(days=1)
    
    pred_df = pd.DataFrame(predictions)
    
    print(f"生成了 {len(pred_df)} 天的预测")
    print(f"预测申购总金额: {pred_df['purchase'].sum():.2f}")
    print(f"预测赎回总金额: {pred_df['redeem'].sum():.2f}")
    
    return pred_df

def save_optimized_results(pred_df, original_pred_df=None):
    """
    保存优化结果，并与原始结果进行对比
    """
    print("\n正在保存优化结果...")
    
    # 选择需要的列保存为最终预测
    result_df = pred_df[['report_date', 'purchase', 'redeem']].copy()
    result_df.to_csv('factor_result_optimized.csv', index=False, encoding='utf-8')
    
    print("优化结果已保存到 factor_result_optimized.csv")
    
    # 保存详细结果供分析 - 包含所有因子和权重信息
    detail_df = pred_df[['report_date', 'weekday', 'day', 'period',
                        'weekday_purchase_factor', 'day_purchase_factor', 'period_purchase_factor',
                        'weekday_purchase_weight', 'day_purchase_weight',
                        'purchase_simple_multiply', 'purchase_weighted_avg', 
                        'purchase_confidence_weighted', 'purchase_period_adjusted',
                        'purchase', 'redeem']].copy()
    
    detail_df.to_csv('factor_result_optimized_detail.csv', index=False, encoding='utf-8')
    print("详细优化结果已保存到 factor_result_optimized_detail.csv")
    
    print("\n优化预测结果预览:")
    print(result_df.head(10))
    
    # 如果有原始结果，进行对比
    if original_pred_df is not None:
        print("\n与原始预测的对比:")
        comparison = pd.DataFrame({
            'report_date': result_df['report_date'],
            '原始申购预测': original_pred_df['purchase'].values,
            '优化申购预测': result_df['purchase'].values,
            '差异': result_df['purchase'].values - original_pred_df['purchase'].values,
            '差异率%': ((result_df['purchase'].values - original_pred_df['purchase'].values) / original_pred_df['purchase'].values * 100).round(2)
        })
        print(comparison.head(10))

def analyze_optimization_impact(pred_df):
    """分析优化的影响"""
    print("\n" + "="*80)
    print("优化影响分析")
    print("="*80)
    
    # 不同预测方案的统计
    print("\n各预测方案的总体预测结果:")
    print(f"简单相乘: 申购={pred_df['purchase_simple_multiply'].sum():.0f}, "
          f"赎回={pred_df['redeem_simple_multiply'].sum():.0f}")
    print(f"加权平均: 申购={pred_df['purchase_weighted_avg'].sum():.0f}, "
          f"赎回={pred_df['redeem_weighted_avg'].sum():.0f}")
    print(f"置信度加权: 申购={pred_df['purchase_confidence_weighted'].sum():.0f}, "
          f"赎回={pred_df['redeem_confidence_weighted'].sum():.0f}")
    print(f"周期调整: 申购={pred_df['purchase_period_adjusted'].sum():.0f}, "
          f"赎回={pred_df['redeem_period_adjusted'].sum():.0f}")
    
    # 按月内周期分析
    print("\n按月内周期的预测分析:")
    for period in ['上旬', '中旬', '下旬']:
        period_data = pred_df[pred_df['period'] == period]
        print(f"{period}: 平均申购={period_data['purchase'].mean():.0f}, "
              f"平均赎回={period_data['redeem'].mean():.0f}")

def main():
    """主函数"""
    setup_encoding()
    
    print("=" * 80)
    print("周期因子分析预测申购和赎回金额 - 优化版本")
    print("=" * 80)
    
    try:
        # 1. 加载和预处理数据
        df = load_and_preprocess_data()
        
        # 2. 按日期聚合数据
        daily_data = aggregate_daily_data(df)
        
        # 3. 计算高级周期因子
        (weekday_factors, day_factors, period_factors,
         overall_purchase_mean, overall_redeem_mean) = calculate_advanced_factors(daily_data)
        
        # 4. 生成优化预测
        pred_df = generate_september_predictions_optimized(
            weekday_factors, day_factors, period_factors,
            overall_purchase_mean, overall_redeem_mean
        )
        
        # 5. 保存优化结果
        save_optimized_results(pred_df)
        
        # 6. 分析优化效果
        analyze_optimization_impact(pred_df)
        
        print("\n" + "=" * 80)
        print("优化预测完成！")
        print("=" * 80)
        
    except Exception as e:
        print(f"程序执行出错: {e}")
        import traceback
        traceback.print_exc()

if __name__ == "__main__":
    main()
